/*
 *   Copyright (C) 2011 David Swords
 *	
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package comm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;

import org.apache.commons.net.telnet.TelnetClient;

public class Communicator {
	
	private final String class_details = "Communicator.";
	private TelnetClient tc;
	private boolean read;
	private boolean write;
	private int defaultPort;
	private String defaultIP;
	public String output;
	public String input;
	
	public Communicator(String IP) {
		final String constructor_details = class_details + "Communicator(String)";
		chk(IP.isEmpty(), "Given IP was blank.", constructor_details);
		tc = new TelnetClient();
		chk(tc == null, "TelnetClient is equal to null, please initialize.", constructor_details);
		defaultPort = 1337;
		defaultIP = IP;
		read = false;
		write = false;
		output = "";
		input = "";
	}
	
	private void chk(final boolean expression, final String message, final String location) {
		if(expression) {
			System.err.println("#############################################################");
			System.err.println(message);
			System.err.println(location);
			System.err.println("#############################################################");
			System.exit(1);
		}
	}
	
	public boolean isConnected() {
		final String method_details = class_details + "isConnected() returns boolean";
		chk(tc == null, "TelnetClient is equal to null, please initialize.", method_details);
		return tc.isConnected();
	}
	
	public void connect() {
		final String method_details = class_details + "connect() returns void";
		chk(tc == null, "TelnetClient is equal to null, please initialize.", method_details);
		
		try {
			tc.connect(defaultIP, defaultPort);
		} catch (SocketException e) {
			chk(true, e.getMessage(), method_details);
		} catch (IOException e) {
			chk(true, e.getMessage(), method_details);
		}
			
		(new Thread(new TelnetReader())).start();
		(new Thread(new TelnetWriter())).start();
	}
	
	public void disconnect() {
		final String method_details = class_details + "disconnect() returns void";
		chk(tc == null, "TelnetClient is equal to null, please initialize.", method_details);
		
		try {
			read = false;
			write = false;
			tc.disconnect();
		} catch (IOException e) {
			chk(true, e.getMessage(), method_details);
		}
	}
	
	public void write(String out) {
		final String method_details = class_details + "write(String) returns void";
		chk(out.isEmpty(), "The string out is empty.", method_details);
		output = out;
	}
	
	public String read() {
		final String method_details = class_details + "read() returns String";
		if(input.isEmpty()) {
			return "<< 00 00 00 00 00 00 00 00 00 00";
		} else {
			return input;
		}
	}
	
	@SuppressWarnings("unused")
	private String CRC16CCITT(String in) {
		final String method_details = class_details + "CRC16CCITT(String) returns String";
		chk(in.isEmpty(), "The string in is empty.", method_details);
		
		int crc = 0xFFFF;
        int polynomial = 0x1021;

        byte[] bytes = in.getBytes();

        for (byte b : bytes) {
            for (int i = 0; i < 8; i++) {
                boolean bit = ((b   >> (7-i) & 1) == 1);
                boolean c15 = ((crc >> 15    & 1) == 1);
                crc <<= 1;
                if (c15 ^ bit) crc ^= polynomial;
             }
        }

        crc &= 0xffff;
        String output = Integer.toHexString(crc);
        chk(output.isEmpty(), "The string output is empty.", method_details);
        
        return "CRC16-CCITT = " + output;
	}
	
	private class TelnetReader implements Runnable {
		
		final String subclass_details = "TelnetReader.";
		
		@Override
		public void run() {
			final String method_details = class_details + subclass_details + "run() returns void";
			chk(tc == null, "TelnetClient is equal to null, please initialize.", method_details);
			InputStream instr = tc.getInputStream();

			byte[] buff = new byte[1024];
			int ret_read = 0;
			read = true;

	            while(read) {
	            	try {
	            		ret_read = instr.read(buff);
			        } catch (Exception e) {
			            chk(true, e.getMessage(), method_details);
			        }
			        
	                if(ret_read > 0) {	
	                    input = new String(buff, 0, ret_read);
	                }
	            }
		}
	}
	
	private class TelnetWriter implements Runnable {
		
		final String subclass_details = "TelnetWriter.";

		@Override
		public void run() {
			final String method_details = class_details + subclass_details + "run() returns void";
			chk(tc == null, "TelnetClient is equal to null, please initialise.", method_details);
			OutputStream outstr = tc.getOutputStream();
			write = true;
						
            while (write) {
            	byte[] buff = output.getBytes();
            	
            	try {
            		try {
            			Thread.sleep(100);
            		} catch (InterruptedException e) {
            			chk(true, e.getMessage(), method_details);
            		}
            		outstr.write(buff);
            		outstr.flush();
            	} catch (Exception e) {
            		chk(true, e.getMessage(), method_details);
    	        }
            }
		}
	}
}